package main

import (
	"errors"
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

/*
日志级别
1.Debug
2.Trace
3.Info
4.Warning
5.Error
6.Fatal

日志支持开关控制

完整的日志记录要包含有时间、行号、文件名、日志级别、日志信息

日志文件要切割


*/

func main() {
	//fileobj, err := os.OpenFile("./xx.log", os.O_APPEND|os.O_RDWR|os.O_WRONLY|os.O_CREATE, 0644)
	//
	//if err != nil {
	//	fmt.Printf("打开失败", err)
	//	return
	//}
	//
	//log.SetOutput(fileobj)
	//
	//for {
	//	log.Println("这是一条测试的日志")
	//	time.Sleep(time.Second * 3)
	//}
	log := NewLog("Debug")
	for {
		log.Debug("这是一条Debug日志")
		log.Info("这是一条Info日志")
		id := 1000
		name := "张三"
		log.Warning("这是一条Warning日志, id:%d,name:%s", id, name)
		log.Error("这是一条Error日志")
		log.Fatal("这是一条Fatal日志")
		time.Sleep(time.Second * 2)
	}
}

type LogLevel uint16

const (
	UKNOWN LogLevel = iota
	DEBUG
	TRACE
	INFO
	WARNING
	ERROR
	FATAL
)

func paseLogLevel(s string) (LogLevel, error) {
	s = strings.ToLower(s) // 全部转换成大写
	switch s {
	case "debug":
		return DEBUG, nil
	case "trace":
		return TRACE, nil
	case "ingo":
		return INFO, nil
	case "warning":
		return WARNING, nil
	case "error":
		return ERROR, nil
	case "fatal":
		return FATAL, nil
	default:
		err := errors.New("日志级别错误")
		return UKNOWN, err
	}
}

// 日志类
type Logger struct {
	level LogLevel
}

// 构造函数
func NewLog(levelStr string) Logger {
	level, err := paseLogLevel(levelStr)
	if err != nil {
		panic(err)
	}
	return Logger{
		level,
	}
}

func (l Logger) enable(logLevel LogLevel) bool {
	return logLevel >= l.level
}

func getLogString(lv LogLevel) string {
	switch lv {
	case DEBUG:
		return "DEBUG"
	case TRACE:
		return "TRACE"
	case INFO:
		return "INFO"
	case WARNING:
		return "WARNING"
	case ERROR:
		return "ERROR"
	default:
		return "UkNOW"
		break
	}
	return "DEBUG"
}

func log(lv LogLevel, format string, a ...interface{}) {
	msg := fmt.Sprintf(format, a...)
	now := time.Now()
	funcName, fileName, lineNo := getInfo(2)

	fmt.Printf("[%s] [%s] %s\n", now.Format("2006-01-02 15:04:06"), getLogString(lv), msg, funcName, fileName, lineNo)
	f := NewFileLogger("Debug", "./", "zhoulinwang.log", 10*1024*1024)
	if f.checkSize(f.fileObj) {
		// 需要切割日志文件
		// 1. 关闭当前日志文件
		f.fileObj.Close()
		// 2. 备份 rename xx.log -> xx.log.bak201908031709
		nowStr := time.Now().Format("20060102150405000")
		logName := path.Join(f.filePath, f.fileName)                             // 拿到当前文件的日志文件
		newLogName := fmt.Sprintf("%s/%s.bak%s", f.filePath, f.fileName, nowStr) // 拼接一个日志文件备份名字
		os.Rename(logName, newLogName)
		// 3.打开新的日志文件
		fileobj, err := os.OpenFile(logName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
		if err != nil {
			fmt.Printf("opennew err:", err)
			return
		}
		// 将日志赋值给
		f.fileObj = fileobj
	}
	// 记录日志
	fmt.Fprintf(f.fileObj, "[%s] [%s] %s\n", now.Format("2006-01-02 15:04:06"), getLogString(lv), msg, funcName, fileName, lineNo)
	if lv >= ERROR {
		fmt.Fprintf(f.errorFileObj, "[%s] [%s] %s\n", now.Format("2006-01-02 15:04:06"), getLogString(lv), msg, funcName, fileName, lineNo)
	}
}

func (l Logger) Debug(format string, a ...interface{}) {
	if l.enable(DEBUG) {
		log(DEBUG, format, a...)
	}
}

func (l Logger) Trace(format string, a ...interface{}) {
	if l.enable(TRACE) {
		log(TRACE, format, a...)
	}
}

func (l Logger) Info(format string, a ...interface{}) {
	if l.enable(INFO) {
		log(INFO, format, a...)
	}
}

func (l Logger) Warning(format string, a ...interface{}) {
	if l.enable(WARNING) {
		log(WARNING, format, a...)
	}
}

func (l Logger) Error(format string, a ...interface{}) {
	if l.enable(ERROR) {
		log(ERROR, format, a...)
	}
}

func (l Logger) Fatal(format string, a ...interface{}) {
	if l.enable(FATAL) {
		log(FATAL, format, a...)
	}
}

func getInfo(skip int) (funcName, fileName string, lineNo int) {
	pc, file, line, ok := runtime.Caller(skip) // 调用该方法的层级
	if !ok {
		fmt.Println("失败-------")
		return
	}
	functionName := runtime.FuncForPC(pc).Name()
	fmt.Println(functionName)
	fmt.Println(path.Base(file))
	fmt.Println(line)
	funcName = runtime.FuncForPC(pc).Name()
	fileName = path.Base(file)
	lineNo = line
	funcName = strings.Split(funcName, ".")[1]
	return
}

// 文件写入

type FileLogger struct {
	Level        LogLevel
	filePath     string // 日志保存的路径
	fileName     string // 日志保存的文件名
	fileObj      *os.File
	errorFileObj *os.File
	maxFileSize  int64
}

// NewFileLogger 构造函数
func NewFileLogger(levelStr, fp, fn string, maxSize int64) *FileLogger {
	logLevel, err := paseLogLevel(levelStr)
	if err != nil {
		panic(err)
	}
	f1 := &FileLogger{
		Level:       logLevel,
		filePath:    fp,
		fileName:    fn,
		maxFileSize: maxSize,
	}
	errz := f1.initFile()
	if err != nil {
		fmt.Println(errz)
		return nil
	}
	return f1
}

func (f *FileLogger) initFile() error {
	fullFileName := path.Join(f.filePath, f.fileName)
	filobj, err := os.OpenFile(fullFileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Printf("openerr", err)
		return err
	}
	errFilobj, err := os.OpenFile(fullFileName+".err", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Printf("openerr", err)
		return err
	}
	f.fileObj = filobj
	f.errorFileObj = errFilobj
	defer filobj.Close()
	defer errFilobj.Close()
	return nil
}

// 获取文件大小

func (f *FileLogger) checkSize(file *os.File) bool {
	fileInfo, err := file.Stat()
	if err != nil {
		fmt.Println(err)
		return false
	}
	// 如果当前文件大小大于等于日志文件最大值
	fileInfo.Size() > f.maxFileSize
	return
}

、、